[DllImport("mpr.dll")]
[return:MarshalAs(UnmanagedType.U4)]
static extern int WNetGetUniversalName(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
Declare Function WNetGetUniversalName Lib "advapi32.dll" (TODO) As TODO
const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
const int ERROR_MORE_DATA = 234;
const int NOERROR = 0;
using System.Runtime.InteropServices;
static string GetUniversalName(string localPath)
{
// The return value.
string retVal = null ;
// The pointer in memory to the structure.
IntPtr buffer = IntPtr.Zero;
// Wrap in a try/catch block for cleanup.
try
{
// First, call WNetGetUniversalName to get the size.
int size = 0;
// Make the call.
// Pass IntPtr.Size because the API doesn't like null, even though
// size is zero. We know that IntPtr.Size will be
// aligned correctly.
int apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, (IntPtr) IntPtr.Size, ref size);
// If the return value is not ERROR_MORE_DATA, then
// raise an exception.
if (apiRetVal != ERROR_MORE_DATA)
// Throw an exception.
throw new Win32Exception(apiRetVal);
// Allocate the memory.
buffer = Marshal.AllocCoTaskMem(size);
// Now make the call.
apiRetVal = WNetGetUniversalName(localPath, UNIVERSAL_NAME_INFO_LEVEL, buffer, ref size);
// If it didn't succeed, then throw.
if (apiRetVal != NOERROR)
// Throw an exception.
throw new Win32Exception(apiRetVal);
// Now get the string. It's all in the same buffer, but
// the pointer is first, so offset the pointer by IntPtr.Size
// and pass to PtrToStringAuto.
retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));
}
finally
{
// Release the buffer.
Marshal.FreeCoTaskMem(buffer);
}
// First, allocate the memory for the structure.
// That's all folks.
return retVal;
}
Note by Günter Prossliner 2005-08-29
PtrToStringAuto doesn't work on my system. I have tested in on a clean WindowsXP professional maschine. The PtrToStringAuto function just returns garbage. After stepping into the function with the debugger I was looking a the Memory - Windows pointing to the address of "buffer". What I've seen there was a "normal" zero - terminated ANSI String.
After i have changed
retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));
into
retVal = Marshal.PtrToStringAnsi(new IntPtr(buffer.ToInt64() + IntPtr.Size), size);
all worked fine.
If you take a look at the IL - Code of Marshal.PtrToStringAuto(IntPtr) you can see that this function calls 'kernel32:lstrlen' and 'kernel32:lstrcpy' using a buffer (StringBuilder) with the size of lstrlen.
The code of Marshal.PtrToStringAuto(IntPtr, Int32 size) looks quite different: It checks out the Marshal.SystemDefaultCharSize field and simply calls 'PtrToStringAnsi' or 'PtrToStringUni'.
Please add some!
/* This needs cleaning up -- errr but I'm paid by the hour so I don't have the time. I mostly ripped the wrapper and modified it. It provides an alternative to pointer arithmetic though...*/
/*
typedef struct _REMOTE_NAME_INFO
{
LPTSTR lpUniversalName;
LPTSTR lpConnectionName;
LPTSTR lpRemainingPath;
} REMOTE_NAME_INFO;
*/
[StructLayout(LayoutKind.Sequential)]
struct _REMOTE_NAME_INFO
{
public IntPtr lpUniversalName;
public IntPtr lpConnectionName;
public IntPtr lpRemainingPath;
}
public struct RemoteNameInfo
{
public string universalName;
public string connectionName;
public string remainingPath;
}
public class WNet
{
const int UNIVERSAL_NAME_INFO_LEVEL = 0x00000001;
const int REMOTE_NAME_INFO_LEVEL = 0x00000002;
const int ERROR_MORE_DATA = 234;
const int ERROR_NOT_CONNECTED = 2250;
const int NOERROR = 0;
[DllImport("mpr.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.U4)]
private static extern int WNetGetUniversalNameW(
string lpLocalPath,
[MarshalAs(UnmanagedType.U4)] int dwInfoLevel,
IntPtr lpBuffer,
[MarshalAs(UnmanagedType.U4)] ref int lpBufferSize);
public static RemoteNameInfo GetRemoteNameInfo(string localPath)
{
// The return value.
RemoteNameInfo retVal;
_REMOTE_NAME_INFO rni;
// The pointer in memory to the structure.
IntPtr buffer = IntPtr.Zero;
// Wrap in a try/catch block for cleanup.
try
{
// First, call WNetGetUniversalName to get the size.
int size = 0;
// Make the call.
// Pass IntPtr.Size because the API doesn't like null, even though
// size is zero. We know that IntPtr.Size will be
// aligned correctly.
int apiRetVal = WNetGetUniversalNameW(localPath, REMOTE_NAME_INFO_LEVEL, (IntPtr)IntPtr.Size, ref size);
// if the return value is ERROR_NOT_CONNECTED, then
// this is a local path
if (apiRetVal == ERROR_NOT_CONNECTED)
{
retVal = new RemoteNameInfo();
retVal.connectionName = Path.GetPathRoot(localPath);
retVal.remainingPath = localPath.Substring(Path.GetPathRoot(localPath).Length);
retVal.universalName = localPath;
return retVal;
}
// If the return value is not ERROR_MORE_DATA, then
// raise an exception.
if (apiRetVal != ERROR_MORE_DATA)
// Throw an exception.
throw new System.ComponentModel.Win32Exception();
// Allocate the memory.
buffer = Marshal.AllocCoTaskMem(size);
// Now make the call.
apiRetVal = WNetGetUniversalNameW(localPath, REMOTE_NAME_INFO_LEVEL, buffer, ref size);
// If it didn't succeed, then throw.
if (apiRetVal != NOERROR)
// Throw an exception.
throw new System.ComponentModel.Win32Exception();
// Now get the string. It's all in the same buffer, but
// the pointer is first, so offset the pointer by IntPtr.Size
// and pass to PtrToStringAuto.
//retVal = Marshal.PtrToStringAuto(new IntPtr(buffer.ToInt64() + IntPtr.Size));
rni = (_REMOTE_NAME_INFO)Marshal.PtrToStructure(buffer, typeof(_REMOTE_NAME_INFO));
retVal.connectionName = Marshal.PtrToStringAuto(rni.lpConnectionName);
retVal.remainingPath = Marshal.PtrToStringAuto(rni.lpRemainingPath);
retVal.universalName = Marshal.PtrToStringAuto(rni.lpUniversalName);
return retVal;
}
finally
{
// Release the buffer.
Marshal.FreeCoTaskMem(buffer);
}
// First, allocate the memory for the structure.
// That's all folks.
}
}
by Mike Gerbi
12/05/2007
Imports System.Runtime.InteropServices
Namespace Custom.Win32
'You can add more functionality from this DLL in this namespace and import it in future projects to get everything.
Public Class MPR_DotNET_Version
Private bufSize As Integer = 1000
Private myPath As String = ""
Private lpBuffer As IntPtr = Marshal.AllocHGlobal(bufSize)
Public Enum INFO_LEVEL As Integer
UNIVERSAL_NAME_INFO_LEVEL = 1
REMOTE_NAME_INFO_LEVEL = 2
End Enum
Public Sub New(ByVal path As String)
myPath = path
End Sub
Public Function GetUnivseralName() As String
Dim uname As New UNIVERSAL_NAME_INFO
Try
Dim result As String = myPath
Dim ret As Int32 = GetUName(result, INFO_LEVEL.UNIVERSAL_NAME_INFO_LEVEL, lpBuffer, bufSize)
If ret = 0 Then
Marshal.PtrToStructure(lpBuffer, uname)
ElseIf ret = 2250 Then
Throw New ArgumentException("Not Connected")
End If
Catch ex As Exception
MsgBox(ex.Message)
Finally
Marshal.FreeHGlobal(lpBuffer)
End Try
Return uname.UniversalName.ToString
End Function
<DllImport("mpr.dll", Entrypoint:="WNetGetUniversalName", CharSet:=CharSet.Auto, SetLastError:=False)> _
Private Shared Function GetUName(ByVal Path As String, ByVal outName As INFO_LEVEL, _
ByVal bObj As IntPtr, ByRef bSize As Integer) As Integer
End Function
End Class
<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto), Serializable()> Class UNIVERSAL_NAME_INFO
<MarshalAs(UnmanagedType.LPTStr)> Public UniversalName As String
<MarshalAs(UnmanagedType.LPTStr)> Public ConnectionName As String
<MarshalAs(UnmanagedType.LPTStr)> Public RemainingPath As String
End Class
End Namespace
Imports ScanningTools.Custom.Win32
Public Sample Class
Private path as String
Public Sub GetPath()
Dim result As DialogResult = mainFolderBrowserDialog.ShowDialog
If result = Windows.Forms.DialogResult.OK Then
Dim dirInfo As DirectoryInfo = New DirectoryInfo(mainFolderBrowserDialog.SelectedPath)
path = mainFolderBrowserDialog.SelectedPath
'Here I get the UNC path and use it later in another routine.
Dim UNI As MPR_DotNET_Version = New MPR_DotNET_Version(path)
path = UNI.GetUnivseralName
For Each f As FileInfo In dirInfo.GetFiles
If f.Extension = ".txt" Then
TextFileListView.Items.Add(f.Name)
ElseIf f.Extension = ".pdf" Then
PDFListView.Items.Add(f.Name)
End If
Next
ElseIf result = Windows.Forms.DialogResult.Cancel Then
Me.Close()
Else
Throw New System.Exception
End If
Catch ex As System.Exception
MsgBox(ex.Message)
WriteToErrorLog(ex)
End Try
End Sub
Do you know one? Please contribute it!